﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Management;

namespace UtilityToolsCollect.ObjectsLibrary.ProcessManage
{
    public sealed class ProcessManage : IProcessManage,IDisposable
    {
        private ConcurrentQueue<ProcessTask> ProcessTaskQueue;
        private List<ProcessShell> ProcessList;
        private ProcesMangeScheduleAbstract? Schedule;

        private ProcessManage()
        {
            ProcessList = new List<ProcessShell>();
            ProcessTaskQueue = new ConcurrentQueue<ProcessTask>();
        }
        private void m_AddTask(ProcessTask task) => ProcessTaskQueue.Enqueue(task);
        private void m_ClearTask()
        {
            //ProcessTaskQueue.Clear();
            if (Schedule!.IsRunning) throw new ProcessManageException("要清空任务队列，必须先停止调度执行");
            while (ProcessTaskQueue.TryDequeue(out _)) ;
        }
        private void Next()
        {
            ScheduleStart:
            if (!ProcessTaskQueue.TryDequeue(out ProcessTask task))
            {
                if (ProcessTaskQueue.Count > 0)
                    goto ScheduleStart;
                else { Schedule!.Stop(); return; }
            }
            ProcessShell? shell = ProcessList.FirstOrDefault(p => !p.IsExistTask);
            if (shell is null)
            {
                shell = new ProcessShell();
                ProcessList.Add(shell);
            }

            task = SetOnClossed(task);
            shell.ExecuteAsyn(task);
        }

        /// <summary>
        /// 在原有的OnClossed前插入[ProcessTaskComplete]
        /// </summary>
        /// <param name="task"></param>
        /// <returns></returns>
        private ProcessTask SetOnClossed(ProcessTask task)
        {
            Action<ProcessShell>? Source = task.OnClossed;
            task.OnClossed = (p) => { ProcessTaskComplete(); Source?.Invoke(p); };
            return task;
        }
        private void ProcessTaskComplete() 
        {
            if (ProcessTaskQueue.Count > 0 || ProcessList.Where(p => p.IsExistTask).Count() > 0)
                return;
            Schedule!.Stop();
            ProcessList.Clear();
        }
        private void AllKill()
        {
            if (Schedule != null || Schedule!.IsRunning)
                Schedule.Stop();
            if (ProcessList.Count > 0)
            {
                ProcessList.ForEach(p => p.Kill());
                ProcessList.Clear();
            }
        }

        ConcurrentQueue<ProcessTask> IProcessManage.TaskQueue => ProcessTaskQueue;
        List<ProcessShell> IProcessManage.ShellList => ProcessList;
        void IProcessManage.Next() => Next();

        private static ProcessManage? m_Current;
        private static ProcessManage Current => m_Current ??= new ProcessManage();


        public static void AddTask(ProcessTask task) => Current.m_AddTask(task);
        public static void ClearTask() => Current.m_ClearTask();
        public static void InitSetting(ProcesMangeScheduleAbstract Schedule) { Current.Schedule = Schedule; }
        public static void InitSetting(double IntervalTime = 10000, bool EnableMaxCPUThreadLimit = true, int maxCPUThread = 6)=>
            Current.Schedule = new DefaultProcesMangeSchedule(Current, IntervalTime, EnableMaxCPUThreadLimit, maxCPUThread);
        public static void Start() { if (Current.Schedule == null) throw new ProcessManageException("尚未[InitSetting]"); Current.Schedule.StartRunning(); }
        public static void ScheduleStop() { if (Current.Schedule == null) throw new ProcessManageException("尚未[InitSetting]"); if (IsScheduleRuning) Current.Schedule.Stop(); }
        public static void KillAll() => Current.AllKill();
        public static bool IsRuning => Current.ProcessList.Where(p => p.IsRunning).Count() > 0;
        public static bool IsScheduleRuning => Current.Schedule?.IsRunning ?? false;
        public static void DisposCurrent() { Current.Dispose(); m_Current = null; }
        public static void KillChildProcess(Process process)
        {
            int RootID = process.Id;
            HashSet<int> ProcessIDs = new HashSet<int>();
            Queue<int> CurrentIDQueue = new Queue<int>();
            List<Process> Processs = new List<Process>();

            CurrentIDQueue.Enqueue(RootID);
            while (CurrentIDQueue.TryDequeue(out int currentID))
            {
                if (currentID != RootID)
                    ProcessIDs.Add(currentID);

                using (ManagementObjectSearcher searcher = new ManagementObjectSearcher("Select * From Win32_Process Where ParentProcessID=" + currentID))
                {
                    using (ManagementObjectCollection moc = searcher.Get())
                    {
                        foreach (ManagementObject mo in moc)
                        {
                            CurrentIDQueue.Enqueue(Convert.ToInt32(mo["ProcessID"]));
                        }
                    }
                }
            }

            foreach (int id in ProcessIDs)
            {
                using (Process current = Process.GetProcessById(id))
                    current.Kill();
            }
        }

        #region IDisposable
        private bool disposedValue;
        private void dispose()
        {
            if (!disposedValue)
            {
                AllKill();
                if (ProcessTaskQueue != null)
                {
                    //ProcessTaskQueue.Clear();
                    while (ProcessTaskQueue.TryDequeue(out _)) { }
                    ProcessTaskQueue = null!;
                }
                if (ProcessList != null)
                    ProcessList = null!;
                if (Schedule != null)
                {
                    Schedule.Dispose();
                    Schedule = null;
                }
                disposedValue = true;
            }
        }
        ~ProcessManage() => dispose();
        public void Dispose() { dispose(); GC.SuppressFinalize(this); }
        #endregion

    }


    [Serializable]
    public class ProcessManageException : Exception
    {
        public ProcessManageException() { }
        public ProcessManageException(string message) : base(message) { }
        public ProcessManageException(string message, Exception inner) : base(message, inner) { }
        [Obsolete]protected ProcessManageException(
          System.Runtime.Serialization.SerializationInfo info,
          System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
    }
}
